/**
 * 
 */
package com.dalonedrau.jogl.engine;

import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.fixedfunc.GLPointerFunc;

import com.dalonedrau.jogl.opengl.sprite.GroupOfImages;
import com.dalonedrau.jogl.opengl.sprite.GroupOfLines;
import com.dalonedrau.jogl.opengl.sprite.GroupOfText;
import com.dalonedrau.jogl.opengl.sprite.SpriteBase;
import com.dalonedrau.jogl.opengl.sprite.SpriteComparator;
import com.dalonedrau.jogl.opengl.sprite.SpriteTileMap;
import com.jogamp.opengl.util.texture.Texture;

/**
 * @author Donald
 */
public class Render {
	/** the one and only instance of the <code>Render</code> class. */
	private static Render	instance;
	/**
	 * Render Gives access to the singleton instance of {@link Render}.
	 * @return {@link Render}
	 */
	public static Render getInstance() {
		if (Render.instance == null) {
			Render.instance = new Render();
		}
		return Render.instance;
	}
	/** the buffer's current capacity. */
	private int					currentCapacity;
	/** the list of {@link SpriteBase}s to be rendered. */
	private List<SpriteBase>	currentSpriteIteration;
	/** the data buffer used for the VBO. */
	private FloatBuffer			data;
	/** the list of {@link SpriteBase}s to be rendered. */
	private List<SpriteBase>	list;
	/** Hidden constructor. */
	private Render() {
		currentSpriteIteration	= new ArrayList<SpriteBase>(1000);
		data = FloatBuffer.allocate(1024);
		list = new ArrayList<SpriteBase>(1000);
	}
	/**
	 * Registers an entity to be rendered during the next cycle.
	 * @param sprite the {@link SpriteBase} entity
	 */
	public void registerEntity(final SpriteBase sprite) {		
		list.add(sprite);
	}
	/**
	 * Render all sprites and images.
	 * @param gl the {@link GL2} context
	 * @throws Exception if an error occurs
	 */
	public void render(final GL2 gl) throws Exception {
		Collections.sort(list, new SpriteComparator());
		if (GameCanvasListener.getInstance().isVboSupported()) {
			renderVBO(gl);
		} else {
			renderImmediateMode(gl);
		}
		// empty the list after rendering
		list.clear();
	}
	private void renderImmediateMode(final GL2 gl) throws Exception {
		/*
		 * boolean begunQuads = false; int textureId = -999; Texture texture =
		 * null; for (int index = 0; index < list.size(); index++) { SpriteBase
		 * sprite = list.get(index); if (sprite.getPosition() != null) { switch
		 * (sprite.getType()) { case SpriteBase.TYPE_IMAGE: SpriteImage img =
		 * sprite.getImage(); // switch textures if needed if
		 * (img.getTextureId() != textureId) {
		 * if (texture != null) { texture.disable(gl); }
		 * texture = GameController.getInstance().getTexture(
		 * img.getTextureId()); texture.enable(gl); texture.bind(gl); } Vector3
		 * v = sprite.getPosition(); gl.glColor4fv(GlobalMethods.colorToBuffer(
		 * sprite.getColor())); if (!begunQuads) {
		 * gl.glBegin(GL2.GL_QUADS); begunQuads =
		 * true; } gl.glTexCoord2d(img.getLeft(), img.getTop()); gl.glVertex3f(
		 * (float) v.getX(), (float) (v.getY() + sprite.getHeight()), 0f); //
		 * start at left-top corner gl.glTexCoord2d(img.getRight(),
		 * img.getTop()); gl.glVertex3f( (float) (v.getX() + sprite.getWidth()),
		 * (float) (v.getY() + sprite.getHeight()), 0f); // right-top
		 * gl.getGL2().glTexCoord2d(img.getRight(), img.getBottom());
		 * gl.glVertex3f( (float) (v.getX() + sprite.getWidth()), (float)
		 * v.getY(), 0f); // right-bottom
		 * gl.getGL2().glTexCoord2d(img.getLeft(), img.getBottom());
		 * gl.glVertex3f( (float) v.getX(), (float) v.getY(), 0f); //
		 * left-bottom // free up resources for garbage collection img = null; v
		 * = null; break; default: 
		 * textureId = -999; if (texture != null) {
		 * texture.disable(gl); }
		 * texture = null; // rendering a primitive // change color
		 * gl.glColor4fv(GlobalMethods.colorToBuffer( sprite.getColor())); //
		 * begin primitive if (begunQuads) { if (sprite.getType() !=
		 * GL2.GL_QUADS) { gl.getGL2().glEnd();
		 * gl.glBegin(sprite.getType());
		 * begunQuads = false; } } else { /* 
		 * gl.glBegin(sprite.getType().getGlPrimitive()); if
		 * (sprite.getType().getGlPrimitive() == GL2.GL_QUADS) { begunQuads =
		 * true; }
		 *//*
			 * } float[] pts = sprite.getCoordinates(); for (int i = 0; i <
			 * pts.length; i += 2) { float x = pts[i], y = pts[i + 1];
			 * gl.glVertex2f(x, y); } pts = null; if (!begunQuads) {
			 * gl.getGL2().glEnd(); } break; } } //
			 * free up resources for garbage collection sprite = null; } if
			 * (begunQuads) { gl.getGL2().glEnd();
			 * begunQuads = false; } // after rendering, disable the last
			 * texture if (texture != null) {
			 * texture.disable(gl); }
			 * // free up resources for garbage collection textureId = -999;
			 * texture = null;
			 */
	}
	/**
	 * Renders non-textured line vertices using a vertex buffer object.
	 * @param gl2 the {@link GL2} context
	 * @param data the vertex data
	 * @param numVertices the number of vertices rendered
	 */
	private void renderLineVertices(final GL2 gl2, final int numVertices) {
		int vbo = GameCanvasListener.getInstance().getVBO();
		// set the current VBO with this call. the 2nd parameter is the
		// id of the VBO created.
		gl2.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo);

		// get the size of the data
		int numBytes = currentCapacity * GlobalConsts.BYTES_PER_FLOAT;

		// data is stored in the VBO with this call
		gl2.glBufferData(
				GL.GL_ARRAY_BUFFER, // should match the first parameter
				// used when calling glBindBuffer
				numBytes, // the size of the data in bytes
				data, // the buffer data
				GL.GL_DYNAMIC_DRAW);// a "hint" to openGL on how the
		// data will be used. possible
		// values are:
		// GL.GL_STATIC_DRAW - the data will
		// be used many times and never
		// change, so it can be stored on
		// the graphics card
		// GL2.GL_STREAM_DRAW - the data will
		// be used once or a few times, so
		// it's not important to save it on
		// the graphics card
		// GL.GL_DYNAMIC_DRAW - the data
		// will be changed many times and
		// should be stored in memory that
		// can be quickly access by the CPU
		// and the graphics card.
		// animations and shifting vertex
		// data that can change from one
		// frame to the next

		// render the data now

		// tell OpenGL the vertices are coming from the buffer
		gl2.glVertexPointer(
				2, // the number of coordinates per vertex
				GL.GL_FLOAT, // type of numbers used
				6 * GlobalConsts.BYTES_PER_FLOAT, // the distance in bytes
				// between vertices
				0 // the starting position of the data
		// within the VBO
		);
		gl2.glColorPointer(
				4, // the number of values per color
				GL.GL_FLOAT, // type of numbers used
				6 * GlobalConsts.BYTES_PER_FLOAT,
				2 * GlobalConsts.BYTES_PER_FLOAT);
		// unbind all buffers
		gl2.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
		// enable the use of the array
		gl2.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
		gl2.glEnableClientState(GLPointerFunc.GL_COLOR_ARRAY);
		// draw the array
		gl2.glDrawArrays(
				GL2.GL_LINES, // the primitive type
				0, // the first index drawn
				numVertices // the number of vertices per shape
		);
		// disable arrays
		gl2.glDisableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
		gl2.glDisableClientState(GLPointerFunc.GL_COLOR_ARRAY);
	}
	/**
	 * Renders non-textured quad vertices using a vertex buffer object.
	 * @param gl2 the {@link GL2} context
	 * @param data the vertex data
	 * @param numVertices the number of vertices rendered
	 */
	private void renderQuadVertices(final GL2 gl2, final int numVertices) {
		int vbo = GameCanvasListener.getInstance().getVBO();
		// set the current VBO with this call. the 2nd parameter is the
		// id of the VBO created.
		gl2.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo);

		// get the size of the data
		int numBytes = currentCapacity * GlobalConsts.BYTES_PER_FLOAT;

		// data is stored in the VBO with this call
		gl2.glBufferData(
				GL.GL_ARRAY_BUFFER, // should match the first parameter
				// used when calling glBindBuffer
				numBytes, // the size of the data in bytes
				data, // the buffer data
				GL.GL_DYNAMIC_DRAW);// a "hint" to openGL on how the
		// data will be used. possible
		// values are:
		// GL.GL_STATIC_DRAW - the data will
		// be used many times and never
		// change, so it can be stored on
		// the graphics card
		// GL2.GL_STREAM_DRAW - the data will
		// be used once or a few times, so
		// it's not important to save it on
		// the graphics card
		// GL.GL_DYNAMIC_DRAW - the data
		// will be changed many times and
		// should be stored in memory that
		// can be quickly access by the CPU
		// and the graphics card.
		// animations and shifting vertex
		// data that can change from one
		// frame to the next

		// render the data now

		// tell OpenGL the vertices are coming from the buffer
		gl2.glVertexPointer(
				2, // the number of coordinates per vertex
				GL.GL_FLOAT, // type of numbers used
				6 * GlobalConsts.BYTES_PER_FLOAT, // the distance in bytes
				// between vertices
				0 // the starting position of the data
		// within the VBO
		);
		gl2.glColorPointer(
				4, // the number of values per color
				GL.GL_FLOAT, // type of numbers used
				6 * GlobalConsts.BYTES_PER_FLOAT,
				2 * GlobalConsts.BYTES_PER_FLOAT);
		// unbind all buffers
		gl2.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
		// enable the use of the array
		gl2.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
		gl2.glEnableClientState(GLPointerFunc.GL_COLOR_ARRAY);
		// draw the array
		gl2.glDrawArrays(
				GL2.GL_QUADS, // the primitive type
				0, // the first index drawn
				numVertices // the number of vertices per shape
		);
		// disable arrays
		gl2.glDisableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
		gl2.glDisableClientState(GLPointerFunc.GL_COLOR_ARRAY);
	}
	/**
	 * Renders textured quad vertices using a vertex buffer object.
	 * @param gl2 the {@link GL2} context
	 * @param textureId the if the texture rendered from
	 * @param numVertices the number of vertices rendered
	 * @throws Exception if an error occurs
	 */
	private void renderTexturedQuadVertices(final GL2 gl2, final int textureId,
			final int numVertices) throws Exception {
		int vbo = GameCanvasListener.getInstance().getVBO();
		// set the current VBO with this call. the 2nd parameter is the
		// id of the VBO created.
		gl2.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo);

		// get the size of the data
		int numBytes = currentCapacity * GlobalConsts.BYTES_PER_FLOAT;

		// data is stored in the VBO with this call
		gl2.glBufferData(
				GL.GL_ARRAY_BUFFER, // should match the first parameter
				// used when calling glBindBuffer
				numBytes, // the size of the data in bytes
				data, // the buffer data
				GL.GL_DYNAMIC_DRAW);// a "hint" to openGL on how the
		// data will be used. possible
		// values are:
		// GL.GL_STATIC_DRAW - the data will
		// be used many times and never
		// change, so it can be stored on
		// the graphics card
		// GL2.GL_STREAM_DRAW - the data will
		// be used once or a few times, so
		// it's not important to save it on
		// the graphics card
		// GL.GL_DYNAMIC_DRAW - the data
		// will be changed many times and
		// should be stored in memory that
		// can be quickly access by the CPU
		// and the graphics card.
		// animations and shifting vertex
		// data that can change from one
		// frame to the next

		// render the data now

		// tell OpenGL the vertices are coming from the buffer
		gl2.glVertexPointer(
				2, // the number of coordinates per vertex
				GL.GL_FLOAT, // type of numbers used
				8 * GlobalConsts.BYTES_PER_FLOAT, // the distance in bytes
				// between vertices
				0 // the starting position of the data
		// within the VBO
		);
		gl2.glTexCoordPointer(
				2, // the number of coordinates per array element
				GL.GL_FLOAT, // type of numbers used
				8 * GlobalConsts.BYTES_PER_FLOAT, // the distance in bytes
				// between coordinate sets
				2 * GlobalConsts.BYTES_PER_FLOAT);
		gl2.glColorPointer(
				4, // the number of values per color
				GL.GL_FLOAT, // type of numbers used
				8 * GlobalConsts.BYTES_PER_FLOAT, // the distance in bytes
				// between colors
				4 * GlobalConsts.BYTES_PER_FLOAT);
		// unbind all buffers
		gl2.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
		// enable the use of the array
		gl2.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
		gl2.glEnableClientState(GLPointerFunc.GL_TEXTURE_COORD_ARRAY);
		gl2.glEnableClientState(GLPointerFunc.GL_COLOR_ARRAY);

		// bind the texture
		Texture texture = GameController.getInstance().getTexture(textureId);
		texture.enable(gl2);
		texture.bind(gl2);
		// draw the array
		gl2.glDrawArrays(
				GL2.GL_QUADS, // the primitive type
				0, // the first index drawn
				numVertices // the number of vertices per shape
		);
		texture.disable(gl2);
		texture = null;
		// disable arrays
		gl2.glDisableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
		gl2.glDisableClientState(GLPointerFunc.GL_TEXTURE_COORD_ARRAY);
		gl2.glDisableClientState(GLPointerFunc.GL_COLOR_ARRAY);
	}
	/**
	 * Renders non-textured triangle vertices using a vertex buffer object.
	 * @param gl2 the {@link GL2} context
	 * @param data the vertex data
	 * @param numVertices the number of vertices rendered
	 */
	private void renderTriangleVertices(final GL2 gl2, final int numVertices) {
		int vbo = GameCanvasListener.getInstance().getVBO();
		// set the current VBO with this call. the 2nd parameter is the
		// id of the VBO created.
		gl2.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo);

		// get the size of the data
		int numBytes = currentCapacity * GlobalConsts.BYTES_PER_FLOAT;

		// data is stored in the VBO with this call
		gl2.glBufferData(
				GL.GL_ARRAY_BUFFER, // should match the first parameter
				// used when calling glBindBuffer
				numBytes, // the size of the data in bytes
				data, // the buffer data
				GL.GL_DYNAMIC_DRAW);// a "hint" to openGL on how the
		// data will be used. possible
		// values are:
		// GL.GL_STATIC_DRAW - the data will
		// be used many times and never
		// change, so it can be stored on
		// the graphics card
		// GL2.GL_STREAM_DRAW - the data will
		// be used once or a few times, so
		// it's not important to save it on
		// the graphics card
		// GL.GL_DYNAMIC_DRAW - the data
		// will be changed many times and
		// should be stored in memory that
		// can be quickly access by the CPU
		// and the graphics card.
		// animations and shifting vertex
		// data that can change from one
		// frame to the next

		// render the data now

		// tell OpenGL the vertices are coming from the buffer
		gl2.glVertexPointer(
				2, // the number of coordinates per vertex
				GL.GL_FLOAT, // type of numbers used
				6 * GlobalConsts.BYTES_PER_FLOAT, // the distance in bytes
				// between vertices
				0 // the starting position of the data
		// within the VBO
		);
		gl2.glColorPointer(
				4, // the number of values per color
				GL.GL_FLOAT, // type of numbers used
				6 * GlobalConsts.BYTES_PER_FLOAT,
				2 * GlobalConsts.BYTES_PER_FLOAT);
		// unbind all buffers
		gl2.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
		// enable the use of the array
		gl2.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
		gl2.glEnableClientState(GLPointerFunc.GL_COLOR_ARRAY);
		// draw the array
		gl2.glDrawArrays(
				GL.GL_TRIANGLES, // the primitive type
				0, // the first index drawn
				numVertices // the number of vertices per shape
		);
		// disable arrays
		gl2.glDisableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
		gl2.glDisableClientState(GLPointerFunc.GL_COLOR_ARRAY);
	}
	private void renderVBO(final GL2 gl) throws Exception {
		int textureId = -999;
		int indexTextureId = -999;
		int currentType = GlobalConsts.INT_999, sprites = 0;
		currentCapacity = 0;
		currentSpriteIteration.clear();
		for (int index = 0; index < list.size(); index++) {
			if (list.get(index).isDebug()) {
				System.out.println("render " + index + " "
						+ list.get(index) + "::" 
						+ list.get(index).getPosition());
			}
			if (currentType == GlobalConsts.INT_999) {
				currentType = list.get(index).getType();
			}
			if (list.get(index).getType() == SpriteBase.TYPE_00_BASIC_IMAGE
					|| list.get(index).getType() 
					== SpriteBase.TYPE_01_GROUP_IMAGE
					|| list.get(index).getType() 
					== SpriteBase.TYPE_03_GROUP_TEXT
					|| list.get(index).getType() == SpriteBase.TYPE_09_TILE_MAP
					|| list.get(index).getType() == SpriteBase.TYPE_10_PARTICLE)
			{
				indexTextureId =
						Sprite.getInstance().getTextureId(list.get(index));
			} else {
				// no texture needed
				indexTextureId = -GlobalConsts.INT_999;
			}
			if (textureId == -GlobalConsts.INT_999) {
				textureId = indexTextureId;
			}
			// what type of sprite is this?
			if (list.get(index).getType() == currentType
					&& currentType != SpriteBase.TYPE_00_BASIC_IMAGE
					&& currentType != SpriteBase.TYPE_01_GROUP_IMAGE
					&& currentType != SpriteBase.TYPE_03_GROUP_TEXT
					&& currentType != SpriteBase.TYPE_09_TILE_MAP
					&& currentType != SpriteBase.TYPE_10_PARTICLE) {
				// dealing with non-texture
				// adding more of same type
				if (currentType == SpriteBase.TYPE_08_TRIANGLES) {
					sprites++;
					currentCapacity += GlobalConsts.INT_018; // 18 floats for a
					// triangle primitive
				} else if (currentType == SpriteBase.TYPE_06_QUADS) {
					sprites++;
					currentCapacity += GlobalConsts.INT_024; // 24 floats for a
					// quad primitive					
				} else if (currentType == SpriteBase.TYPE_04_LINE) {
					sprites++;
					currentCapacity += GlobalConsts.INT_012; // 12 floats for a
					// line primitive					
				} else if (currentType == SpriteBase.TYPE_02_GROUP_LINES) {
					sprites += 
						((GroupOfLines) list.get(index)).getNumberOfImages();
					currentCapacity += (GlobalConsts.INT_012 
							* ((GroupOfLines) list.get(index)).getNumberOfImages());
				}
				currentSpriteIteration.add(list.get(index));
			} else if (list.get(index).getType() == currentType
					&& (currentType == SpriteBase.TYPE_00_BASIC_IMAGE
							|| currentType == SpriteBase.TYPE_01_GROUP_IMAGE
							|| currentType == SpriteBase.TYPE_03_GROUP_TEXT
							|| currentType == SpriteBase.TYPE_09_TILE_MAP
							|| currentType == SpriteBase.TYPE_10_PARTICLE)
					&& indexTextureId == textureId) {
				// dealing with image from same texture
				// adding more of same type and texture
				if (currentType == SpriteBase.TYPE_00_BASIC_IMAGE
						|| currentType == SpriteBase.TYPE_10_PARTICLE) {
					sprites++; 
					// 32 floats for a textured quad
					currentCapacity += GlobalConsts.INT_032;
				} else if (currentType == SpriteBase.TYPE_01_GROUP_IMAGE) {
					sprites += 
						((GroupOfImages) list.get(index)).getNumberOfImages();
					currentCapacity += (GlobalConsts.INT_032 
							* ((GroupOfImages) list.get(index)).getNumberOfImages());
				} else if (currentType == SpriteBase.TYPE_03_GROUP_TEXT) {
					sprites += 
						((GroupOfText) list.get(index)).getNumCharacters();
					currentCapacity += (GlobalConsts.INT_032 
							* ((GroupOfText) list.get(index)).getNumCharacters());
				} else if (currentType == SpriteBase.TYPE_09_TILE_MAP) {
					int num = 
						((SpriteTileMap) list.get(index)).getNumberOfImagesVisible();
					sprites += num;
					currentCapacity += (GlobalConsts.INT_032 * num);
				}
				currentSpriteIteration.add(list.get(index));
			} else {
				// changing type or texture
				// render the last list
				if (currentSpriteIteration.size() > 0) {
					renderVBOData(gl, currentType, sprites, textureId);
				}

				// reset data
				currentType = list.get(index).getType();
				currentSpriteIteration.clear();
				currentCapacity = 0;
				sprites = 0;
				textureId = indexTextureId;
				// start a new list
				// increments the number of sprites in the list
				// and the current list capacity
				switch (currentType) {
				case SpriteBase.TYPE_00_BASIC_IMAGE:
				case SpriteBase.TYPE_10_PARTICLE:
					sprites++;
					currentCapacity += GlobalConsts.INT_032; // 32 floats for a
															// textured quad
					break;
				case SpriteBase.TYPE_01_GROUP_IMAGE:
					sprites += 
						((GroupOfImages) list.get(index)).getNumberOfImages();
					currentCapacity += (GlobalConsts.INT_032 
							* ((GroupOfImages) list.get(index)).getNumberOfImages());
					break;
				case SpriteBase.TYPE_02_GROUP_LINES:
					sprites += 
						((GroupOfLines) list.get(index)).getNumberOfImages();
					currentCapacity += (GlobalConsts.INT_012 
							* ((GroupOfLines) list.get(index)).getNumberOfImages());
					break;
				case SpriteBase.TYPE_03_GROUP_TEXT:
					sprites += 
						((GroupOfText) list.get(index)).getNumCharacters();
					currentCapacity += (GlobalConsts.INT_032 
							* ((GroupOfText) list.get(index)).getNumCharacters());
					break;
				case SpriteBase.TYPE_09_TILE_MAP:
					int num = 
						((SpriteTileMap) list.get(index)).getNumberOfImagesVisible();
					sprites += num;
					currentCapacity += (GlobalConsts.INT_032 * num);
					break;
				case SpriteBase.TYPE_08_TRIANGLES:
					sprites++;
					currentCapacity += GlobalConsts.INT_018; // 18 floats for a
					// triangle primitive
					break;
				case SpriteBase.TYPE_06_QUADS:
					sprites++;
					currentCapacity += GlobalConsts.INT_024; // 24 floats for a
					// quad primitive
					break;
				case SpriteBase.TYPE_04_LINE:
					sprites++;
					currentCapacity += GlobalConsts.INT_012; // 12 floats for a
					// line primitive
					break;
				}
				currentSpriteIteration.add(list.get(index));
			}
		} // end for
		if (currentSpriteIteration.size() > 0) { // render any images remaining
			renderVBOData(gl, currentType, sprites, textureId);
		}
	}
	/**
	 * Renders data from the vertex buffer object.
	 * @param gl2 the {link GL2} context
	 * @param type the type of images being rendered
	 * @param numSprites the number of sprites being rendered
	 * @param textureId the id of the texture used, if applicable
	 * @throws Exception if an error occurs
	 */
	private void renderVBOData(final GL2 gl2, final int type,
			final int numSprites, final int textureId) throws Exception {
		switch (type) {
		case SpriteBase.TYPE_00_BASIC_IMAGE:
		case SpriteBase.TYPE_01_GROUP_IMAGE:
		case SpriteBase.TYPE_03_GROUP_TEXT:
		case SpriteBase.TYPE_09_TILE_MAP:
		case SpriteBase.TYPE_10_PARTICLE:
			wrapData(type);
			renderTexturedQuadVertices(gl2, textureId,
					numSprites * GlobalConsts.INT_004);
			break;
		case SpriteBase.TYPE_08_TRIANGLES:
			wrapData(type);
			renderTriangleVertices(gl2, numSprites * GlobalConsts.INT_003);
			break;
		case SpriteBase.TYPE_06_QUADS:
			wrapData(type);
			renderQuadVertices(gl2, numSprites * GlobalConsts.INT_004);
			break;
		case SpriteBase.TYPE_02_GROUP_LINES:
		case SpriteBase.TYPE_04_LINE:
			wrapData(type);
			renderLineVertices(gl2, numSprites * 2);
			break;
		}
	}
	private void wrapData(final int type) throws Exception {
		// create room for the new data in the buffer
		if (data.capacity() < currentCapacity) {
			// buffer is too small, allocate a new buffer
			data = FloatBuffer.allocate(currentCapacity);
		} else {
			// buffer has enough capacity, clear old buffer
			data.clear();
		}
		float[] src = new float[currentCapacity];
		int arrOffset = 0;
		for (int i = 0; i < currentSpriteIteration.size(); i++) {
			switch (type) {
			case SpriteBase.TYPE_00_BASIC_IMAGE:
			case SpriteBase.TYPE_10_PARTICLE:
				if (currentSpriteIteration.get(i).getFloatStoreNumber() > 0) {
					System.arraycopy(
							currentSpriteIteration.get(i).getFloatStore(), 
							0, 
							src, 
							arrOffset, 
							currentSpriteIteration.get(i).getFloatStoreNumber()
					);
				} else {
					Sprite.getInstance()
					.convertBasicSpriteToTexturedQuadDuringRender(
							currentSpriteIteration.get(i),
							src,
							arrOffset
							);					
				}				
				arrOffset += 
					currentSpriteIteration.get(i).getFloatStoreNumber();
				break;
			case SpriteBase.TYPE_01_GROUP_IMAGE:
				if (currentSpriteIteration.get(i).getFloatStoreNumber() > 0) {
					System.arraycopy(
							currentSpriteIteration.get(i).getFloatStore(), 
							0, 
							src, 
							arrOffset, 
							currentSpriteIteration.get(i).getFloatStoreNumber()
					);					
				} else {
					Sprite.getInstance()
					.convertGroupOfImagesToTexturedQuadDuringRender(
							(GroupOfImages) currentSpriteIteration.get(i),
							src,
							arrOffset
							);
				}
				arrOffset += 
					currentSpriteIteration.get(i).getFloatStoreNumber();
				break;
			case SpriteBase.TYPE_02_GROUP_LINES:
				if (currentSpriteIteration.get(i).getFloatStoreNumber() > 0) {
					System.arraycopy(
							currentSpriteIteration.get(i).getFloatStore(), 
							0, 
							src, 
							arrOffset, 
							currentSpriteIteration.get(i).getFloatStoreNumber()
					);					
				} else {
					Sprite.getInstance()
					.convertGroupOfLinesToLinesDuringRender(
							(GroupOfLines) currentSpriteIteration.get(i),
							src,
							arrOffset
							);
				}
				arrOffset += 
					currentSpriteIteration.get(i).getFloatStoreNumber();
				break;
			case SpriteBase.TYPE_03_GROUP_TEXT:
				if (currentSpriteIteration.get(i).getFloatStoreNumber() > 0) {
					System.arraycopy(
							currentSpriteIteration.get(i).getFloatStore(), 
							0, 
							src, 
							arrOffset, 
							currentSpriteIteration.get(i).getFloatStoreNumber()
					);					
				} else {
					Sprite.getInstance()
					.convertGroupOfTextToTexturedQuadDuringRender(
							(GroupOfText) currentSpriteIteration.get(i),
							src,
							arrOffset
							);
				}
				arrOffset += 
					currentSpriteIteration.get(i).getFloatStoreNumber();
				break;
			case SpriteBase.TYPE_09_TILE_MAP:
				if (currentSpriteIteration.get(i).getFloatStoreNumber() > 0) {
					System.arraycopy(
							currentSpriteIteration.get(i).getFloatStore(), 
							0, 
							src, 
							arrOffset, 
							currentSpriteIteration.get(i).getFloatStoreNumber()
					);					
				} else {
					Sprite.getInstance()
					.convertSpriteTileMapToTexturedQuad(
							(SpriteTileMap) currentSpriteIteration.get(i),
							src,
							arrOffset
							);
				}
				arrOffset += 
					currentSpriteIteration.get(i).getFloatStoreNumber();
				break;
			case SpriteBase.TYPE_08_TRIANGLES:
				if (currentSpriteIteration.get(i).getFloatStoreNumber() > 0) {
					System.arraycopy(
							currentSpriteIteration.get(i).getFloatStore(), 
							0, 
							src, 
							arrOffset, 
							currentSpriteIteration.get(i).getFloatStoreNumber()
					);
				} else {
					Sprite.getInstance().convertBasicSpriteToTriangleDuringRender(
							currentSpriteIteration.get(i),
							src,
							arrOffset
							);
				}
				arrOffset += 
					currentSpriteIteration.get(i).getFloatStoreNumber();
				break;
			case SpriteBase.TYPE_06_QUADS:
				if (currentSpriteIteration.get(i).getFloatStoreNumber() > 0) {
					System.arraycopy(
							currentSpriteIteration.get(i).getFloatStore(), 
							0, 
							src, 
							arrOffset, 
							currentSpriteIteration.get(i).getFloatStoreNumber()
					);
				} else {
					Sprite.getInstance().convertBasicSpriteToQuadDuringRender(
							currentSpriteIteration.get(i),
							src,
							arrOffset
							);					
				}			
				arrOffset += 
					currentSpriteIteration.get(i).getFloatStoreNumber();
				break;
			case SpriteBase.TYPE_04_LINE:
				if (currentSpriteIteration.get(i).getFloatStoreNumber() > 0) {
					System.arraycopy(
							currentSpriteIteration.get(i).getFloatStore(), 
							0, 
							src, 
							arrOffset, 
							currentSpriteIteration.get(i).getFloatStoreNumber()
					);
				} else {
					Sprite.getInstance().convertBasicSpriteToLineDuringRender(
							currentSpriteIteration.get(i),
							src,
							arrOffset
							);					
				}			
				arrOffset += 
					currentSpriteIteration.get(i).getFloatStoreNumber();
				break;
			}
		}
		// data.rewind();
		data.put(src);
		src = null;
		data.flip();
	}
}
